package com.example.blog.util;

import com.example.blog.exception.BizAssertUtils;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Classname MinioStroage
 * @Description TODO
 * @Version 1.0.0
 * @Date 2023/8/20 16:25
 * @Created by wlh12
 */
@Repository
@Slf4j
public class MinioStroage {

    @Autowired
    private MinioClient minioClient;

    /**
     * 上传
     *
     * @param file
     * @param bucket
     * @return
     */
    public String upload(MultipartFile file, String bucket) {
        String fileName = buildFileName(file.getOriginalFilename());
        try (InputStream inputStream = file.getInputStream()) {
            if (!bucketExists(bucket)) {
                createBucket(bucket);
            }
            PutObjectArgs putObjectArgs = PutObjectArgs
                    .builder()
                    .bucket(bucket)
                    .object(fileName)
                    .stream(inputStream, inputStream.available(), -1)
                    .build();
            minioClient.putObject(putObjectArgs);
        } catch (Exception e) {
            log.error("minio上传失败：", e);
            throw new RuntimeException(e);
        }
        return fileName;
    }

    /**
     * 批量上传
     *
     * @param files
     * @param bucket
     * @return
     */
    public String[] upload(MultipartFile[] files, String bucket) {
        String[] strings = new String[files.length];
        int i = 0;
        for (MultipartFile file : files) {
            String res = upload(file, bucket);
            strings[i++] = res;
        }
        return strings;
    }

    /**
     * 批量下载
     *
     * @param fileNames
     * @param bucket
     * @return
     */
    public List<byte[]> download(String[] fileNames, String bucket) {
        List<byte[]> list = new ArrayList<>();
        for (String fileName : fileNames) {
            byte[] download = download(fileName, bucket);
            list.add(download);
        }
        return list;
    }

    /**
     * 批量删除
     *
     * @param fileNames
     * @param bucket
     */
    public void delete(String[] fileNames, String bucket) {
        for (String fileName : fileNames) {
            delete(fileName, bucket);
        }
    }

    /**
     * 下载
     *
     * @param fileName
     * @param bucket
     * @return
     */
    public byte[] download(String fileName, String bucket) {
        BizAssertUtils.isTrue(bucketExists(bucket), "该文件不存在！");
        GetObjectArgs getObjectArgs = GetObjectArgs.builder()
                .bucket(bucket)
                .object(fileName)
                .build();
        try (InputStream inputStream = minioClient.getObject(getObjectArgs)) {
            return inputStream.readAllBytes();
        } catch (Exception e) {
            log.error("minio获取文件失败：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 将指定文件下载保存到本机file
     *
     * @param filename
     * @param bucket
     * @param file
     */
    public void saveLocal(String filename, String bucket, File file) {
        try {
            DownloadObjectArgs downloadObjectArgs = DownloadObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename)
                    .filename(file.getPath())
                    .build();

            minioClient.downloadObject(downloadObjectArgs);
        } catch (Exception e) {
            log.error("从Minio保存本地失败：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 删除文件
     *
     * @param fileName
     * @param bucket
     */
    public void delete(String fileName, String bucket) {
        BizAssertUtils.isTrue(bucketExists(bucket), "该文件不存在！");
        RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder()
                .bucket(bucket)
                .object(fileName)
                .build();
        try {
            minioClient.removeObject(removeObjectArgs);
        } catch (Exception e) {
            log.error("minio删除文件失败：", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取文件对象的信息（不下载文件），例如标签，大小，最后修改时间，保留时间信息等等。
     *
     * @param filename
     * @param bucket
     * @return
     */
    public StatObjectResponse getStatObjectInfo(String filename, String bucket) {
        try {
            StatObjectArgs statObjectArgs = StatObjectArgs.builder()
                    .bucket(bucket)
                    .object(filename)
                    .build();

            return minioClient.statObject(statObjectArgs);
        } catch (Exception e) {
            log.error("minio获取文件信息失败：", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 将指定文件从srcBucket复制到tarBucket中
     * @param filename
     * @param srcBucket
     * @param tarBucket
     */
    public void copyToBucket(String filename, String srcBucket, String tarBucket) {

        try {
            boolean b = bucketExists(srcBucket);
            BizAssertUtils.isTrue(b, "srcBucket 不存在");
            b = bucketExists(tarBucket);
            if (!b) {
                createBucket(tarBucket);
            }
            CopyObjectArgs copyObjectArgs = CopyObjectArgs.builder()
                    .bucket(tarBucket)
                    .object(filename)
                    .source(
                            CopySource.builder()
                                    .bucket(srcBucket)
                                    .object(filename)
                                    .build()
                    )
                    .build();
            minioClient.copyObject(copyObjectArgs);
        } catch (Exception e) {
            log.error("minio copy失败：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取该文件的预览地址
     * @param filename
     * @param bucket
     * @return
     */
    public String previewUrl(String filename, String bucket) {
        try {
            GetPresignedObjectUrlArgs getPresignedObjectUrlArgs = GetPresignedObjectUrlArgs.builder()
                    .bucket(bucket)
                    .object(filename)
                    .method(Method.GET) // 设置预览的请求方法
                    .build();
            return minioClient.getPresignedObjectUrl(getPresignedObjectUrlArgs);
        } catch (Exception e) {
            log.error("minio获取预览url失败：", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 检查bucket桶是否存在
     *
     * @param bucket
     * @return
     */
    public boolean bucketExists(String bucket) {
        BucketExistsArgs existsArgs = BucketExistsArgs
                .builder()
                .bucket(bucket)
                .build();
        try {
            return minioClient.bucketExists(existsArgs);
        } catch (Exception e) {
            log.error("检查bucket失败：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建bucket桶
     *
     * @param bucket
     */
    public void createBucket(String bucket) {
        MakeBucketArgs makeBucketArgs = MakeBucketArgs
                .builder()
                .bucket(bucket)
                .build();
        try {
            minioClient.makeBucket(makeBucketArgs);
        } catch (Exception e) {
            log.error("创建bucket失败：", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取当前Minio服务器的所有存储桶
     *
     * @return
     */
    public List<Bucket> getBucketList() {
        try {
            return minioClient.listBuckets();
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            log.error("获取bucket列表失败：", e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取当前Minio服务器的所有存储桶
     *
     * @param queryParam 额外的查询参数
     * @param headers    额外的请求头信息
     * @return
     */
    public List<Bucket> getBucketList(Map<String, String> queryParam, Map<String, String> headers) {
        try {
            ListBucketsArgs.Builder builder = ListBucketsArgs.builder();
            if (queryParam != null && !queryParam.isEmpty()) {
                builder.extraQueryParams(queryParam);
            }

            if (headers != null && !headers.isEmpty()) {
                builder.extraHeaders(headers);
            }
            ListBucketsArgs listBucketsArgs = builder.build();
            return minioClient.listBuckets(listBucketsArgs);
        } catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
                 InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
                 XmlParserException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 用于生成文件名称
     *
     * @param fileName
     * @return
     */
    public String buildFileName(String fileName) {
        int index = fileName.lastIndexOf(".");
        String suffix = fileName.substring(index);

        return System.currentTimeMillis() + CodeUtil.createNumberCode(3) + suffix;
    }
}
